home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / lib / annotate.pro < prev    next >
Text File  |  1997-07-08  |  40KB  |  1,338 lines

  1. ; $Id: annotate.pro,v 1.16 1997/01/15 03:11:50 ali Exp $
  2. ;
  3. ; Copyright (c) 1993-1997, Research Systems, Inc.  All rights reserved.
  4. ;    Unauthorized reproduction prohibited.
  5. ;
  6. ; Parameter definitions:
  7. ; For all objects:
  8. ; 0    p0_x
  9. ; 1    p0_y
  10. ; 2    p1_x
  11. ; 3    p1_y
  12. ; 4    color index
  13. ; 5    thickness
  14. ; 6    linestyle
  15. ; 7    size
  16. ; 8    fill style (0=none, 1 = solid, 2 = lines
  17. ; 9    fill space
  18. ; 10    fill angle
  19. ; 11    npoints / nchars
  20. ; 12    alignment
  21. ; 13    font
  22. ; 14    orientation
  23. ; 15    head size
  24. ; 17    line/arrow/solid_arrow
  25. ; 18    eccen
  26. ; 19    object type
  27. ; 20    interpolation
  28. ;
  29. ; Modes:
  30. ;    0 Text
  31. ;    1 Lines/Arrows
  32. ;    2 Polylines/Polygons, Drag
  33. ;    3 Circles
  34. ;    4 Squares
  35.  
  36.  
  37.  
  38. pro xmgr_fake, top, window
  39. ; simulate the xmanager routine for a non-widgetized environment.
  40. ; deliver events from the ann widget as well as motion and button
  41. ; events from the selected idl window.
  42.  
  43. buttons = 0
  44. ix0 = -1
  45. iy0 = -1
  46.  
  47. while 1 do begin
  48.     ev = widget_event(top, BAD_ID = bad, /NOWAIT)   ;Widget event?
  49.     if bad ne 0L then return            ;Gone? Done.
  50.     if ev.id ne 0L then annotate_event, ev $    ;Dispatch it.
  51.     else begin                ;Check for mouse
  52.     if !d.window ne window then wset, window
  53.     CURSOR, ix, iy, /DEV, /NOWAIT
  54.         if (ix < iy) ge 0 then begin    ;In window?
  55.         j = !err            ;New buttons
  56.         i = j xor buttons        ;changed buttons
  57.         if i ne 0 then $        ;Button change...
  58.             ANN_DRAW_EVENT, { ANN_FAKE_EVENT, $
  59.             x:ix, y:iy, press: i and j, release: i and buttons }, top $
  60.         else if ((ix ne ix0) or (iy ne iy0)) then $
  61.             ANN_DRAW_EVENT, { ANN_FAKE_EVENT, $
  62.             x:ix, y:iy, press: 0L, release: 0L }, top
  63.         ix0 = ix
  64.         iy0 = iy
  65.         buttons = j
  66.             endif               ;ix, iy >= 0
  67.     endelse                 ;no widget event
  68.   endwhile
  69. end
  70.  
  71.  
  72.  
  73. function b_button, a    ;Return a 1 bit deep bitmap given a byte image.
  74.             ;width of image MUST be a multiple of 8.
  75. s = size(a)
  76. b = bytarr(s[1]/8, s[2])
  77. if (s[1] and 7) ne 0 then $
  78.     message, 'B_BUTTON: image width must be a multiple of 8.'
  79. subs = lindgen(n_elements(b)) * 8
  80. for ibit = 0,7 do b = b or byte(2^ibit) * (a[subs+ibit] ne 0b)
  81. return, reverse(b,2)
  82. end
  83.  
  84.  
  85. FUNCTION ann_closest, xy, n, pos    ;return index of closest point
  86. d = (xy[0,0:n-1]-pos[0])^2 + (xy[1,0:n-1]-pos[1])^2
  87. junk = min(d, i)
  88. return, i
  89. end
  90.  
  91. FUNCTION ann_closest_obj, st
  92. WIDGET_CONTROL, st.objlist, GET_UVALUE=l  ;Object list
  93. dmin = 1e6
  94. pos = st.pos
  95. psize = n_elements(st.p)            ;Elements/object
  96. nrows = n_elements(l)/psize
  97. if n_elements(l) le 1 then return, 0
  98. for i=0L, nrows-1 do begin  ;Each object
  99.     p = l[*,i]            ;Ith object
  100.     k = i
  101.     n = long(p[11])
  102.     case p[19] of            ;Which object?
  103. 0:  BEGIN            ;A mess...
  104.     x = p[2]        ;Text width
  105.     dx = cos(p[14]* !dtor) * x
  106.     dy = sin(p[14]* !dtor) * x
  107.         q = [p[0] + dx * (0.5-p[12]), p[1] + dy * (0.5-p[12])]
  108.     i = i + (n + psize - 1)/psize    ;Next object
  109.     ENDCASE
  110. 1:  q = [p[0]+p[2], p[1]+p[3]]/2.   ;Lines
  111. 2:  BEGIN            ;Polygon
  112.     j = psize * (i+1)
  113.     xy = reform(l[j: j+2*n-1], 2, n)  ;Extract points
  114.     xmin=min(xy[0,*], max=xmax)
  115.     ymin=min(xy[1,*], max=ymax)
  116.     q = [xmin+xmax, ymin+ymax]/2.
  117.     i = i + (2*n+psize-1)/psize    ;Next element
  118.     ENDCASE
  119. 3:  q = p[[2,3]]        ;Circle
  120. 4:  q = [p[0]+p[2], p[1]+p[3]]/2.   ;box
  121. else: q = pos + 10        ;Unknown obj, dist = big
  122.     ENDCASE
  123.     d = total((pos - q)^2)    ;Distance...
  124.     if d lt dmin then begin dmin = d & imin = k & endif
  125. ENDFOR
  126.  
  127. if dmin lt 0.04 then return, imin else return, -1
  128. END                ;Ann_closest_obj
  129.  
  130.  
  131.  
  132. PRO ANN_GET_NUM_EVENT, ev
  133. on_ioerror, bad_again
  134. widget_control, ev.id, get_value=v
  135. widget_control, ev.top, get_uvalue=u
  136. val = float(v[0])
  137. if val lt u.minv or val gt u.maxv then goto, bad_again
  138. WIDGET_CONTROL, u.id, SET_VALUE=v[0]    ;Save the correct value
  139. WIDGET_CONTROL, ev.top, /DESTROY    ;Done
  140. return
  141. bad_again:  WIDGET_CONTROL, ev.id, SET_VALUE=''
  142. return
  143. END
  144.  
  145.  
  146. pro CW_CONF_EVENT, ev    ;On any event, save the value and kill it
  147. WIDGET_CONTROL, ev.id, GET_UVALUE=i    ;Button index
  148. WIDGET_CONTROL, ev.top, GET_UVALUE=temp ;Temp storage location
  149. WIDGET_CONTROL, temp, SET_UVALUE=i    ;save choice
  150. WIDGET_CONTROL, ev.top, /DESTROY    ;Kill it
  151. return
  152. END
  153.  
  154.  
  155. FUNCTION CW_CONFIRM, message, choices
  156. ; Make a Modal widget with the given message as text, and with buttons
  157. ;   corresponding to the choices.  Return the index of the button pressed.
  158. ;
  159.     temp = WIDGET_BASE()
  160.     a = WIDGET_BASE(title='Confirmation',/COLUMN, UVALUE=temp, $
  161.                     GROUP_LEADER=temp, /MODAL)
  162.     for i=0, N_ELEMENTS(message)-1 DO junk = widget_label(a, value=message[i])
  163.     b = WIDGET_BASE(a, /ROW)
  164.     for i=0,n_elements(choices)-1 do $
  165.     junk = WIDGET_BUTTON(b, VALUE=choices[i], /NO_REL, UVALUE=i)
  166.     WIDGET_CONTROL, a, /REALIZE
  167.     XMANAGER, 'Confirmation', a, EVENT_HANDLER='CW_CONF_EVENT'
  168.     WIDGET_CONTROL, temp, GET_UVALUE=u, /DESTROY
  169. return, u
  170. end
  171.  
  172.  
  173. FUNCTION ann_get_num, str, id, minv, maxv
  174. ; Get & check  a numeric value from a text widget.  If illegal format,
  175. ; use a modal widget to get a correct value.
  176.     
  177. on_ioerror, bad            ;1st level call
  178. v = float(str[0])
  179. if v lt minv or v gt maxv then goto, bad
  180. return, v            ;OK
  181.  
  182. bad:
  183. temp = WIDGET_BASE()
  184. t = widget_base(title='Invalid Number', /column, $
  185.                 GROUP_LEADER=temp, /MODAL)
  186. WIDGET_CONTROL, t, set_uvalue = { id:id, minv: minv, maxv: maxv} ;Save params
  187. b = WIDGET_LABEL(t, value= 'An invalid number was entered')
  188. b = WIDGET_LABEL(t, value = 'Range = '+string(minv)+' to '+string(maxv))
  189. b = WIDGET_LABEL(t, value= 'Please enter the correct value')
  190. j = WIDGET_TEXT(t, /frame, xsize=10, ysize=1, /EDIT)
  191. WIDGET_CONTROL, t, /REAL
  192. xmanager, 'Invalid Number', t, EVENT_HANDLER = 'ann_get_num_event'
  193. WIDGET_CONTROL, id, GET_VALUE=v    ;The correct value
  194. WIDGET_CONTROL, temp, /DESTROY
  195. return, float(v[0])
  196. end
  197.  
  198.  
  199.  
  200. PRO ann_reset_mode, st, mode, submode, NOCLEAN = noclean
  201. ;Clean out the current object, reset mode
  202.  
  203. if n_elements(mode) le 0 then mode = st.mode    ;Save old mode
  204. if n_elements(submode) le 0 then submode = 0
  205. newmode = st.mode ne mode
  206.  
  207. if newmode then begin
  208.     m = st.mode_bases
  209.     WIDGET_CONTROL, m[n_elements(m)-1], MAP=0    ;Options base
  210.     WIDGET_CONTROL, m[st.mode], MAP=0   ;Unmap old
  211.     WIDGET_CONTROL, st.mode_buttons[mode], SET_BUTTON=1
  212.     if mode ge 2 then begin        ;Set polyfill controls?
  213.     p = st.p
  214.     WIDGET_CONTROL, st.spline_id, SET_VALUE=p[20]
  215.         WIDGET_CONTROL, st.poly_style[mode], SET_VALUE=p[8]
  216.     WIDGET_CONTROL, st.poly_angle[mode], SET_VALUE=p[10]
  217.     WIDGET_CONTROL, st.poly_spacing[mode], SET_VALUE=p[9]
  218.     endif
  219.     st.mode = mode
  220.     WIDGET_CONTROL, m[mode], MAP=1
  221.     wset, st.draw_win        ;Redraw the view window
  222.     device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[1]]
  223.     endif
  224.  
  225. if keyword_set(noclean) eq 0 then begin    ;Remove current object
  226.     st.p[11] = 0
  227.     endif
  228.  
  229. st.open = 0
  230. st.minor_mode = submode
  231. st.p[19] = st.mode
  232. WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=submode
  233. end
  234.  
  235.  
  236. PRO ann_add_object, st            ;Add current object to list
  237. wset, st.backing[1]            ;Make it permanent
  238. p = st.p
  239. ann_draw_object, st, p            ;Draw obj in backing store
  240. wset, st.draw_win
  241. device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[1]] ;To draw window
  242.  
  243. psize = n_elements(p)
  244. if p[19] eq 0 then begin        ;text?
  245.     n = strlen(st.txt)        ;# of chars, Save in an array
  246.     if n eq 0 then return        ;Nothing to save
  247.     p[11] = n
  248.     a = bytarr(psize, (n+psize-1)/psize) ; Addtl bytes
  249.     a[0] = byte(st.txt)
  250.     p = [[p],[float(a)]]
  251. endif else if p[19] eq 2 then begin    ;Polygon?
  252.     n = long(p[11])*2        ;# of pnts required
  253.     a = fltarr(psize, (n+psize-1)/psize)  ;Array into which to insert
  254.     WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
  255.     a[0] = xy[0:n-1]        ;Insert it
  256.     p = [[p], [a]]
  257. endif    
  258.     
  259. WIDGET_CONTROL, st.objlist, GET_UVALUE = l, /NO_COPY
  260. if n_elements(l) gt 1 then p = [[l], [p]]  ;Add to end of list
  261. WIDGET_CONTROL, st.objlist, SET_UVALUE = p, /NO_COPY  ;Add to list
  262. ann_reset_mode, st        ;Reset mode
  263. end                ;Ann_add_object
  264.  
  265.  
  266.  
  267. pro ann_set_controls, st, p    ;Set controls to corresp to object
  268. WIDGET_CONTROL, st.color_id, SET_VALUE=p[4]
  269. WIDGET_CONTROL, st.thick_id, SET_VALUE=p[5]*10.
  270. WIDGET_CONTROL, st.linestyle_id, SET_VALUE=p[6]
  271.  
  272. case p[19] of
  273. 0:    BEGIN        ;Text
  274.     WIDGET_CONTROL, st.txt_size_id, SET_VALUE=p[7]*10.
  275.     WIDGET_CONTROL, st.txt_id, SET_VALUE=st.txt
  276.     WIDGET_CONTROL, st.ori_id, SET_VALUE=(p[14] + 360) mod 360
  277.     WIDGET_CONTROL, st.txt_ali_id, SET_VALUE=p[12]*2
  278.     WIDGET_CONTROL, st.txt_font_id, SET_VALUE=p[13]
  279.     ENDCASE
  280. 1:    BEGIN        ;Line/arrow
  281.     WIDGET_CONTROL, st.line_arrow_id, SET_VALUE=p[17]
  282.     WIDGET_CONTROL, st.head_size_id, SET_VALUE=p[15]*10.
  283.     ENDCASE
  284. 2:    BEGIN    
  285.     WIDGET_CONTROL, st.spline_id, SET_VALUE=p[20]
  286. do_poly: i = long(p[19])
  287.         WIDGET_CONTROL, st.poly_style[i], SET_VALUE=p[8]
  288.     WIDGET_CONTROL, st.poly_angle[i], SET_VALUE=p[10]
  289.     WIDGET_CONTROL, st.poly_spacing[i], SET_VALUE=p[9]
  290.     ENDCASE
  291. 3:     BEGIN        ;Circle
  292.     WIDGET_CONTROL, st.ecc_id, SET_VALUE=(p[18]-1.)*10.
  293.     goto, do_poly
  294.     ENDCASE
  295. 4:    goto, do_poly    ;Rectangle
  296. ENDCASE
  297. END
  298.  
  299.  
  300.  
  301.  
  302. pro ann_refresh_list, st, FROM_SCRATCH=redraw, WINDOW=window, MONO = mono
  303. ; FROM_SCRATCH = redraw from backing store(0)
  304. ; WINDOW = destination window
  305. ; MONO = color index to gray scale translation table
  306.  
  307. if n_elements(mono) le 0 then mono = 0
  308. if n_elements(window) eq 1 then WSET, window
  309.  
  310. if keyword_set(redraw) then $    ;Redraw from scratch?
  311.     device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[0]]
  312.  
  313. WIDGET_CONTROL, st.objlist, GET_UVALUE=l, /NO_COPY
  314. if n_elements(l) le 1 then goto, done    ;Nothing...
  315. s = size(l)
  316. psize = s[1]            ;# of columns
  317. n = n_elements(l) / psize    ;# of rows
  318. for i=0L, n-1 do begin        ;Do each object....
  319.     p = l[*,i]            ;The object
  320.     m = 0            ;# of addtl elements
  321.     k = (i+1) * psize        ;Next row
  322.     if p[19] eq 0 then begin    ;Text
  323.     m = long(p[11])
  324.     ann_draw_object, { txt: string(byte(l[k:k+m-1]))} , p, MONO = mono
  325.     endif else if p[19] eq 2 then begin  ;polygon?
  326.     m = long(p[11])*2
  327.     xy = reform(l[k:k+m-1], 2, m/2)
  328.     ann_draw_object, st, p, xy=xy, MONO= mono
  329.     endif else ann_draw_object, st, p, MONO= mono
  330.     i = i + (m+psize-1)/psize    ;Next row
  331.     endfor
  332. done:  WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY
  333. end
  334.  
  335.  
  336.  
  337. function ann_load_obj, st, obj    ;Load nth object from list.  Remove
  338. ; the object from the list.  return 0 if error, 1 if ok.
  339. ; reload st with object.
  340.  
  341. WIDGET_CONTROL, st.objlist, GET_UVALUE = l, /NO_COPY
  342. if n_elements(l) le 1 then begin
  343. quit:   WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY
  344.    return, 0
  345.    endif
  346. psize = n_elements(st.p)
  347. n = n_elements(l) / psize    ;# of objects
  348. if obj lt 0 or obj ge n then goto, quit  ;There?
  349. p = l[*,obj]
  350. k = (obj+1) * psize
  351. m = 0
  352. mode = fix(p[19])
  353. if mode eq 0 then begin        ;Text?
  354.     m = long(p[11])
  355.     st.txt = string(byte(l[k:k+m-1]))  ;fetch the text
  356. endif else if mode eq 2 then begin    ;Polygon?
  357.     j = long(p[11])
  358.     m = j*2                ;# of elements
  359.     xy = reform(l[k:k+m-1], 2, j)
  360.     WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
  361. endif else m = 0
  362.  
  363. st.p = p            ;Get the object...
  364.  
  365. m = (m+psize-1)/psize        ;# of addtl rows
  366. ; print, 'loaded',obj,', rows ',m,', n =',n
  367. if n eq (m+1) then l = 0 $    ;last object
  368. else BEGIN            ;Remove this object
  369.     v = replicate(1,n)
  370.     v[obj:obj+m] = 0        ;Rows we remove
  371.     l = l[*,where(v)]
  372. ENDELSE
  373.  
  374. WIDGET_CONTROL, st.objlist, SET_UVALUE = l, /NO_COPY  ;Restore list
  375. ann_refresh_list, st, window=st.backing[1], /FROM_SCRATCH
  376. ann_set_controls, st, p        ;Reset the controls
  377. return, 1
  378. end
  379.  
  380.     
  381.  
  382. pro ann_xfer_file, st, file, SAVE=save, LOAD=load
  383. ; Transfer an annotate load/save file.
  384.  
  385. WIDGET_CONTROL, st.objlist, GET_UVALUE=l, /NO_COPY    ;Current object list
  386. psize = n_elements(st.p)
  387. n = n_elements(l) / psize
  388. magic = '414e4e00'XL
  389.  
  390. if keyword_set(save) then begin    ;Save??
  391.     if n eq 0 then begin
  392.     i = CW_CONFIRM('There is nothing to save.', 'OK')
  393.     WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY
  394.     return
  395.     endif
  396.  
  397.     openw, unit, /GET_LUN, file, /XDR  ;Make the file
  398.     writeu, unit, magic, psize    ;Magic number, # of columns
  399.     p = float([ !d.x_size, !d.y_size, !d.x_ch_size, !d.y_ch_size ])  ;Params
  400.     writeu, unit, p
  401.  
  402.     for i=0L, n-1 do begin        ;Write each object
  403.     p = l[*,i]            ;The object
  404.     m = 0                ;# of addtl elements
  405.     k = (i+1) * psize        ;Next row
  406.     writeu, unit, p            ;The array
  407.     if p[19] eq 0 then begin    ;Text?
  408.         m = long(p[11])        ;Strlen
  409.         writeu, unit, byte(l[k:k+m-1])  ;The characters
  410.     endif else if p[19] eq 2 then begin  ;Polygon?
  411.         m = long(p[11])*2
  412.         writeu, unit, l[k:k+m-1]    ;The points
  413.     endif
  414.     i = i + (m+psize-1)/psize
  415.     endfor
  416.     writeu, unit, replicate(-1.0, psize)
  417.     free_lun, unit
  418.     WIDGET_CONTROL, st.objlist, SET_UVALUE=l, /NO_COPY  ;Restore
  419.  
  420. endif else begin                ;LOAD
  421.     openr,unit, /GET_LUN, file, /XDR, ERROR=i  ;Try to open it...
  422.     if i lt 0 then begin        ;No file..
  423.     i = CW_CONFIRM(['File not found or unreadable', file], 'OK')
  424.     return
  425.     endif
  426.     i = 0L            ;Read magic
  427.     columns = 0L
  428.     readu, unit, i, columns
  429.     if i ne magic then begin
  430.     i = CW_CONFIRM(['File is not an annotate data file.', file], 'OK')
  431.     return
  432.     endif
  433.     params = fltarr(4)
  434.     readu, unit, params
  435.  
  436.     while 1 do begin        ;Read each record
  437.     p = fltarr(columns)
  438.     readu, unit, p
  439. ;    print, p[19]
  440.     if p[19] eq -1 then begin    ;Done....
  441.         free_lun, unit
  442.         WIDGET_CONTROL, st.objlist, SET_UVALUE = l, /NO_COPY ;save list
  443.         ann_refresh_list, st, WINDOW=st.backing[1], /FROM_SCRATCH
  444.         wset, st.draw_win
  445.         device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[1]]
  446.         return
  447.         endif
  448.     m = 0L
  449.     if p[19] eq 0 then begin    ;string
  450.         m = long(p[11])        ;# of addtl elements
  451.         a = bytarr(m)
  452.         readu, unit, a
  453.         p = [[p], [fltarr(psize, (m+psize-1)/psize)]]  ;what we add
  454.         p[psize] = a        ;Insert chars....
  455.     endif else if p[19] eq 2 then begin    ;polygon
  456.         m = long(p[11])*2        ;# of addtl elements
  457.         a = fltarr(m)
  458.         readu, unit, a
  459.         p = [[p], [fltarr(psize, (m+psize-1)/psize)]]  ;what we add
  460.         p[psize] = a
  461.     endif
  462. ;        Adjust size for possible expansion....
  463.     if columns lt psize then p = [p, fltarr(columns-psize)] $
  464.     else if psize lt columns then p = p[0:psize-1]
  465.  
  466.     if n_elements(l) gt 1 then l = [[temporary(l)], [p]] $
  467.     else l = p
  468.     endwhile
  469. endelse
  470. end
  471.  
  472. pro annotate_ps, st, draw_ps, include_image
  473.  
  474. tvlct, r,g,b, /get              ;current color table
  475. old = !d.name            ;Current device name
  476.  
  477. file = PICKFILE(FILE='annotate.ps', /WRITE, FILTER='*.ps', /NOCONF)
  478.  
  479. if include_image then begin
  480.     if draw_ps then WSET, st.backing[0] $ ;Original image
  481.     else WSET, st.backing[1]    ;image + our annotations
  482.     a = tvrd()
  483.     endif
  484.  
  485. width = st.ps_width        ;Output width
  486. ch = [!d.x_ch_size, !d.y_ch_size] / float([!d.x_size, !d.y_size])
  487. if st.ps_units then width = width / 2.54    ;From cm to inches
  488. height = width * !d.y_size / !d.x_size    ;Height    
  489.  
  490. print, 'Color=',st.ps_color
  491. SET_PLOT,'PS'
  492. DEVICE, file=file, COLOR=st.ps_color
  493. if st.ps_orien then DEVICE, /LANDSCAPE $
  494. else DEVICE, /PORTRAIT
  495. DEVICE, XSIZE = width, YSIZE=height, /INCHES    ;Set size
  496. tvlct,r,g,b
  497. if st.ps_color eq 0 then $    ;To BW from RGB
  498.     l = bytscl(r * .3 + .6 * g + .11 * b)
  499.  
  500. if include_image then begin    ;Include image with new file?
  501.     if st.ps_color eq 0 then a = l[a]
  502.     tv, a                   ;Output image
  503.     endif
  504.  
  505. if draw_ps then begin
  506.     device, set_char = ch * !d.x_size  ;Proportional char size (aspect = same)
  507.     ann_refresh_list, st, MONO = l
  508.     endif
  509.  
  510. if include_image then device, /close
  511. set_plot, old               ;done
  512. end
  513.  
  514.  
  515. pro annotate_event, ev
  516.  
  517. WIDGET_CONTROL, ev.top, GET_UVALUE = st, /NO_COPY   ;Our data structure
  518. WIDGET_CONTROL, ev.id, GET_UVALUE = u        ;The object uvalue
  519.  
  520. ldr = strmid(u, 0, 1)
  521. IF ldr eq 'M' THEN BEGIN        ;New mode?
  522.     ann_reset_mode, st, fix(strmid(u,1,2)), 0
  523.     goto, done
  524.     ENDIF
  525.  
  526. IF ldr eq '#' THEN BEGIN
  527.     WIDGET_CONTROL, ev.id, GET_VALUE = v
  528.     ldr = '@'
  529. ENDIF
  530.  
  531. IF ldr eq '@' THEN BEGIN        ;Execute string?
  532.     i = EXECUTE(strmid(u,2,100))    ;Execute the string....
  533.     IF strmid(u, 1, 1) eq 'R' THEN BEGIN ;Redraw?
  534. redraw:   if st.open then begin
  535.         wset, st.draw_win
  536.         handle = st.minor_mode eq 1
  537.         p = st.p
  538.         ANN_DRAW_OBJECT, st, p, handle, REFRESH=st.backing[1]
  539.         st.p = p
  540.         st.handle = handle
  541.         ENDIF
  542.     ENDIF        ;Redraw
  543. ENDIF ELSE IF u eq 'Help' THEN BEGIN    ;ldr eq '@'
  544.     xdisplayfile, filepath("annotate.txt", subdir=['help', 'widget']), $
  545. ;    xdisplayfile, 'annotate.txt', $
  546.         title = "Annotate Help", $
  547.         group = ev.top, $
  548.         width = 72, height = 24
  549. ENDIF else if u eq 'Options' THEN BEGIN
  550.     m = st.mode_bases
  551.     WIDGET_CONTROL, m[st.mode], MAP=0
  552.     WIDGET_CONTROL, m[n_elements(m)-1], /MAP
  553. ENDIF else if u eq 'Dismiss' THEN BEGIN
  554.     m = st.mode_bases
  555.     WIDGET_CONTROL, m[n_elements(m)-1], MAP=0
  556.     WIDGET_CONTROL, m[st.mode], MAP=1
  557. ENDIF ELSE IF u eq 'TOPROW' THEN BEGIN
  558.     WIDGET_CONTROL, ev.top, /HOURGLASS
  559.     case ev.value of
  560.    'Exit' : BEGIN
  561.         for i=0,1 do wdelete, st.backing[i]
  562.         widget_control, ev.top, /DESTROY
  563.         return
  564.         ENDCASE
  565.     'Save' : BEGIN
  566.     save_file:       if st.open then ann_add_object, st    ;Add current object?
  567.        ann_xfer_file, st, st.file, /SAVE
  568.        ENDCASE
  569.     'Save As': BEGIN
  570.     st.file = PICKFILE(FILE=st.file, /WRITE)
  571.     goto, save_file
  572.     ENDCASE
  573.     'Load': BEGIN
  574.     st.file = PICKFILE(FILE=st.file, /READ, /MUST_EXIST, FILTER='*.dat')
  575.     ann_xfer_file, st, st.file, /LOAD
  576.     ENDCASE
  577.     'TIFF': BEGIN
  578.     file = PICKFILE(FILE='annotate.tif', /WRITE, FILTER='*.tif', /NOCONF)
  579.     WSET, st.backing[1]
  580.     tvlct, red, green, blue, /GET
  581.     WRITE_TIFF, file, tvrd(/ORDER), 1, RED=red, GREEN=green, BLUE=blue
  582.     ENDCASE
  583.     'GIF': BEGIN
  584.     file = PICKFILE(FILE='annotate.gif', /WRITE, FILTER='*.gif', /NOCONF)
  585.     WSET, st.backing[1]
  586.     WRITE_GIF, file, tvrd(ORDER=0)
  587.     ENDCASE
  588.     'PostScript': annotate_ps, st, 0, 1        ;Postscript bitmap
  589.     'Everything': annotate_ps, st, 1, 1        ;do it with PS commands
  590.     'Objects only': annotate_ps, st, 1, 0
  591.     'Clear': BEGIN
  592.         WIDGET_CONTROL, st.objlist, SET_UVALUE=0  ;Clean up object list
  593.         wset, st.backing[1]        ;redraw all objects except this one
  594.         device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[0]]
  595.         wset, st.draw_win
  596.         device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, st.backing[0]]
  597.         ann_reset_mode, st, st.mode, 0
  598.     ENDCASE
  599.     ENDCASE
  600. ENDIF ELSE IF u eq 'Save' THEN BEGIN
  601.     if st.open THEN ann_add_object, st
  602. ENDIF ELSE help, /st, u, ev
  603.  
  604.  
  605. done: WIDGET_CONTROL, ev.top, SET_UVALUE = st, /NO_COPY
  606. end
  607.  
  608.  
  609. PRO ANN_MOVE_RESIZE, st, mode, first, last
  610.  
  611. if st.open eq 0 then return
  612. handle = st.handle
  613. pos = st.pos
  614. p = st.p
  615. if first then begin        ;Initial hit?
  616.     np = ([2,2,4,4,4])[mode]    ;# of points
  617.         ;distances from handle points and midpoint
  618.     h = handle[*,0:np-1]
  619.     d = [[h], [total(h,2)/np]] - (pos # replicate(1.,np+1))
  620.     d = min(total(d^2, 1), i)    ;Dist squared, get closest
  621.     if i eq np then i = 5    ;always use 5 for last point
  622.     if d lt 0.01 then begin    ;Close enough?
  623.     st.ihandle = i
  624.     if mode eq 0 then begin    ;Text mode?
  625.         if p[7] eq 0.0 then p[7] = 1.0    ;Size
  626.         st.temp[0] = p[7] / p[2]        ;1./(Length of siz=1)
  627.      endif else if mode eq 3 then begin    ;Circle?  Save params.
  628.         c = p[2:3]
  629.         st.temp = [p[0:1] - c, total((pos - c)^2)]  ;dx,dy, len
  630.     endif
  631.         if i ne 5 then st.orig = handle[*,np-1-i] $  ;Anchor point
  632.     else st.orig = pos
  633.     endif else st.ihandle = -1
  634. endif                    ;First
  635.  
  636. ihandle = st.ihandle
  637. if ihandle lt 0 then return        ;A hit?
  638. d = pos - st.orig        ;Movement
  639. ; if d(0) eq 0.0 and d(1) eq 0.0 and first eq 0 then goto, no_move
  640.  
  641. if ihandle eq 5 then begin        ;Move?
  642.     st.orig = pos        ;Reset it
  643.     if mode eq 2 then begin    ;Polygons?
  644.     if p[11] gt 1 THEN BEGIN   ;Polygons
  645.         WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
  646.         xy = xy + (d # replicate(1, p[11]))  ;Translate
  647.         WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
  648.         ENDIF
  649.     endif else p[0] = p[0:3] + [d,d]  ;Just move origin
  650. ENDIF ELSE BEGIN            ;Sizing handle
  651.     if (mode gt 1) then begin        ;Need scale factor?
  652.     s = handle[*,ihandle] - st.orig    ;stretch factor
  653.     if min(abs(s)) lt 0.001 then goto, no_move
  654.     s = (pos - st.orig) / s      ;Scale factor
  655.     if min(abs(s)) lt 0.001 then goto, no_move
  656.     endif
  657.     case mode of
  658. 0:    BEGIN            ;Text
  659.     r = sqrt(total(d^2))        ;Length of line
  660.     if r lt 0.001 then goto, no_move
  661.     p[14] = atan(d[1], d[0]) * !radeg    ;New orientation
  662.     if ihandle eq 0 then p[14] = p[14] + 180.
  663.     p[7] = r * st.temp[0]        ;New size
  664.     k = long(3 * ihandle + 2 * p[12]) ;Subs from 0 to 5.
  665.     p[0] = pos + ([0, -0.5, -1., -1, -0.5, 0])[k] * d
  666.     ENDCASE
  667. 1:    p[ihandle * 2] = pos        ;Line, just move vertex
  668. 2:    if p[11] gt 1 THEN BEGIN    ;Polygon, Scale all verts
  669.         WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
  670.         xy0 = st.orig # replicate(1.0,p[11])
  671.         xy = (xy - xy0) * (s # replicate(1.0,p[11])) + xy0
  672.         WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
  673.         ENDIF
  674. 3:    BEGIN            ;Circle
  675.         c = p[2:3]        ;Center
  676.         r = sqrt(total((pos - c)^2)/st.temp[2])  ;Dist from ctr
  677.         p[0] = st.temp[0:1] * r + c
  678.     ENDCASE
  679. 4:    BEGIN            ;Rectangle
  680.         x0 = [st.orig, st.orig]
  681.         p[0] = (p[0:3] - x0) * s[[0,1,0,1]] + x0
  682.     ENDCASE
  683.     ENDCASE
  684. ENDELSE
  685.  
  686. no_move:
  687.     ANN_DRAW_OBJECT, st, p, handle, REFRESH=st.backing[1]  ;Refresh
  688.     st.p = p
  689.     st.handle = handle
  690. END            ;ANN_MOVE_RESIZE
  691.  
  692.  
  693.  
  694.  
  695. PRO ANN_DRAW_EVENT, ev, ann_base    ;Handle events from drawable
  696.  
  697. if n_elements(ann_base) eq 0 then $
  698.     WIDGET_CONTROL, ev.id, GET_UVALUE = ann_base
  699. WIDGET_CONTROL, ann_base, GET_UVALUE=st, /NO_COPY
  700. mode = st.mode
  701.  
  702. WSET, st.draw_win
  703. if ev.press eq 4 then begin    ;Right button to close object
  704.     if st.open then ann_add_object, st
  705.     goto, skip_it
  706. endif else if ev.press eq 2 then begin
  707.     if st.open eq 0 then goto, skip_it
  708.     i = st.minor_mode eq 0    ;Switch modes
  709.         st.minor_mode = i    ;Reset minor mode
  710.     WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=i
  711.     p = st.p
  712.     ANN_DRAW_OBJECT, st, p, i, REFRESH=st.backing[1]
  713.     st.handle = i
  714.     goto, skip_it
  715. endif
  716.  
  717. pos = [ev.x, ev.y]
  718.  
  719. first = 0
  720. drag = 0
  721. last = 0
  722. if st.buttons eq 0 then begin    ;First time?
  723.   if ev.press ne 1 then begin    ;Move?
  724.     if !x.s[1] ne 0 then pos = convert_coord(pos, /DEVICE, /TO_DATA)
  725.     WIDGET_CONTROL, st.instruct, SET_VALUE=string(pos[0:1])
  726.     goto, skip_it
  727.   endif else begin
  728.     first = 1
  729.     WIDGET_CONTROL, st.instruct, SET_VALUE='Right button to close'
  730.     st.buttons = 1
  731.   endelse
  732. endif else begin        ;Not first
  733.   if ev.release ne 1 then drag = 1 $
  734.   else begin
  735.     last = 1
  736.     st.buttons = 0
  737.   endelse
  738. endelse
  739.  
  740.  
  741. g = st.granularity
  742. pos = round(pos/g)*g / [!d.x_size, !d.y_size]
  743. if drag and (st.pos[0] eq pos[0]) and (st.pos[1] eq pos[1]) then goto, skip_it
  744. st.pos = pos
  745.  
  746.  
  747. if st.minor_mode eq 1 then begin    ;MOVE/RESIZE
  748.     ANN_MOVE_RESIZE, st, mode, first, last
  749.     goto, skip_it
  750. ENDIF ELSE if st.minor_mode eq 2 then begin    ;Select
  751.     if first then begin
  752.     ann_reset_mode, st, st.mode, 2
  753.     k = ann_closest_obj(st)
  754.     if k ge 0 then begin        ;Found one?
  755.        st.temp[0] = k
  756.        if ann_load_obj(st, k) then begin    ;Find it?
  757.         p = st.p
  758.         st.minor_mode = 1    ;Now in move/resize
  759.         ann_reset_mode, st, p[19], 1, /NOCLEAN
  760.         st.open = 1
  761.         h = 1
  762.         wset, st.draw_win
  763.         ANN_DRAW_OBJECT, st, p, h, REFRESH=st.backing[1]
  764.         st.handle = h
  765.         ANN_MOVE_RESIZE, st, st.mode, first, last
  766.         endif
  767.     endif
  768.     endif
  769.     goto, skip_it
  770. ENDIF            ;Minor = 2
  771.  
  772. p = st.p
  773.  
  774. if st.open eq 0 then begin    ;First time
  775.     p[11] = 0
  776.     WIDGET_CONTROL, st.fill_mode_id, SET_VALUE=0
  777.     WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=0
  778.     st.fill_mode = 0
  779.     st.minor_mode = 0
  780.     ENDIF
  781. st.open = 1
  782.  
  783. case mode of
  784. 0: BEGIN            ;Text
  785.     IF first THEN BEGIN
  786.     WIDGET_CONTROL, st.txt_id, GET_VALUE=h
  787.     st.txt = h[0]
  788.     ENDIF
  789. no_name_label:
  790.     if first then p[2] = pos
  791.     p[0] = pos
  792.     h = last
  793.     ANN_DRAW_OBJECT, st, p, h, REFRESH=st.backing[1]
  794.     if last then begin
  795.     st.handle = h
  796.     st.minor_mode = 1        ;Go to move resize mode
  797.     WIDGET_CONTROL, st.minor_mode_id, SET_VALUE=1
  798.     endif
  799.    ENDCASE
  800.  
  801. 1: goto, no_name_label        ;Dragging line or arrow
  802.  
  803.  
  804. 2:  BEGIN            ;Polygon/polyline
  805.     n = long(p[11])
  806.     if n eq 0L THEN BEGIN    ;Initialize line buffer?
  807.     xy = FLTARR(2,256)
  808.     ENDIF ELSE WIDGET_CONTROL, st.xy, GET_UVALUE=xy, /NO_COPY
  809.    case st.fill_mode of        ;What are we doing?
  810.    0: BEGIN        ;Vector /drag mode
  811.     if n_elements(xy) le n*2 then xy = [[xy], [FLTARR(2,n)]]  ;Extend it
  812.     xy[0,n] = st.pos    ;Last point
  813.     if st.buttons then p[11] = n+1     ;Dragging
  814.       ENDCASE        ;Vector    
  815.    1: BEGIN        ;Edit mode
  816.     if n eq 0 then goto, skip_it1
  817.     if first then begin
  818.         if n_elements(xy) le n*2 then xy = [[xy], [FLTARR(2,n)]]
  819.         j = ann_closest(xy, n, pos)
  820.         xy[0,n] = j
  821.     endif else j = xy[0,n]
  822.     xy[0,j] = pos        
  823.       ENDCASE
  824.    2: BEGIN        ;Delete
  825.     if (n le 0) or (last eq 0) then goto, skip_it1
  826.     j = ann_closest(xy, n, pos)    ;Point to delete
  827.     p[11] = n-1
  828.     if n eq 1 then goto, skip_it
  829.     if j eq 0 then xy = xy[*,1:*] $
  830.     else if j eq n-1 then xy = xy[*, 0:n-2] $
  831.     else xy = [[xy[*,0:j-1]], [xy[*, j+1:*]]]
  832.       ENDCASE        ;delete
  833.   ENDCASE        ;st.fill_mode
  834.   WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
  835.   ANN_DRAW_OBJECT, st, p, REFRESH=st.backing[1]
  836.  ENDCASE        ;2
  837.  
  838. 3: goto, no_name_label        ;Circle
  839.  
  840.  
  841. 4: goto, no_name_label        ;Box
  842. ENDCASE
  843.  
  844. skip_it:
  845.     if n_elements(p) gt 1 then st.p = p
  846.     WIDGET_CONTROL, ann_base, SET_UVALUE=st, /NO_COPY
  847.     return
  848. skip_it1:
  849.     WIDGET_CONTROL, st.xy, SET_UVALUE=xy, /NO_COPY
  850.     goto, skip_it
  851. end
  852.  
  853.  
  854.  
  855. pro ANN_DRAW_OBJECT, st, p, handle, REFRESH = refresh, XY=xy, MONO = mono
  856.  
  857. c = long(p[4])            ;Color index
  858. if keyword_set(mono) then c = mono[c]    ;Outputting Postscript for BW?
  859.  
  860. if n_elements(refresh) gt 0 then $  ;Refresh?
  861.     device, copy = [0,0, !d.x_size, !d.y_size, 0, 0, refresh]
  862.  
  863. case p[19] of        ;What type of object
  864. 0: BEGIN        ;Text
  865.     str = '!' + strtrim(fix(p[13])>3,2) + st.txt + '!3'
  866.     xyouts, p[0], p[1], /NORM, str, $
  867.         COLOR = c, CHARSIZE = p[7], $
  868.         ALI = p[12], ORI=p[14], CHARTHICK = p[5], WIDTH=x
  869.     p[2] = x         ;Save width
  870.     if keyword_set(handle) then begin
  871.         dx = cos(p[14]* !dtor) * x
  872.         dy = sin(p[14]* !dtor) * x
  873.         handle = [[ p[0]-p[12] * dx, p[1]-p[12] * dy], $
  874.         [p[0] + (1.-p[12]) * dx, p[1] + (1.-p[12]) * dy]]
  875.         n = 2
  876. do_handle:  PLOTS, handle, /NORM, PSYM=6
  877.         PLOTS, total(handle,2)/n, /NORM, PSYM=6  ;The center
  878.         ENDIF
  879.    ENDCASE
  880.  
  881. 1: BEGIN
  882.     if p[17] ne 0 THEN BEGIN    ;Lines/Arrows
  883.     if p[15] le 0.0 then p[15] = 1.0
  884.     ARROW, p[2], p[3], p[0], p[1], /NORM, $
  885.         COLOR = c, THICK = p[5], SOLID=p[17] eq 2, $
  886.         HSIZE = p[15] * !D.X_SIZE/20.
  887.     ENDIF ELSE PLOTS, p[[2,0]], p[[3,1]], /NORM, COLOR=c, THICK=p[5], $
  888.         LINESTYLE=p[6]
  889.     if keyword_set(handle) then begin
  890.     n = 2
  891.     handle = [[p[0], p[1]], [p[2], p[3]]]
  892.     goto, do_handle
  893.     ENDIF
  894.    ENDCASE
  895.  
  896. 2: BEGIN        ;Polygon
  897.     n = p[11]
  898. do_fill:
  899.     if n gt 1 THEN BEGIN
  900.        if n_elements(xy) le 1 then WIDGET_CONTROL, st.xy, GET_UVALUE = xy
  901.        if (p[19] eq 2) and (p[20] eq 1) and (n gt 2) then $  ;Splines?
  902.            spline_p, reform(xy[0,0:n-1]), reform(xy[1,0:n-1]), $
  903.             x, y, INTERV= 0.01 $
  904.        else begin            ;Not splines
  905.         x = xy[0,0:n-1]
  906.         y = xy[1,0:n-1]
  907.        endelse
  908.        if (p[8] ne 1) or (n eq 2) THEN BEGIN   ;Outline it?
  909.         plots, x, y, /NORM, COLOR = c, $  ;Draw outline
  910.             THICK = p[5], LINESTYLE = p[6]
  911.         if p[8] eq 2 THEN BEGIN  ;line fill?
  912.            plots, x[[n-1,0]], y[[n-1,0]], /NORM, COLOR = c, $ ;Close it
  913.             THICK = p[5], LINESTYLE = p[6]
  914.            if n ge 3 THEN POLYFILL, x, y, /NORM, COLOR = c, $
  915.             THICK = p[5], ORI = p[10], SPACING = p[9]/100.
  916.         ENDIF
  917.       ENDIF ELSE IF N GE 3 THEN $        ;Solid fill
  918.         POLYFILL, x,y, /NORM, COLOR = c
  919.       ENDIF
  920.     if keyword_set(handle) then begin    ;Draw resizing handles
  921.        xmin=min(x, max=xmax)
  922.        ymin=min(y, max=ymax)
  923.        n = 4
  924.        handle = [[xmin, ymin], [xmin, ymax], [xmax,ymin],[xmax, ymax]]
  925.        goto, do_handle
  926.        ENDIF
  927.    ENDCASE
  928. 3: BEGIN        ;Circle/ Ellipse
  929.     a = !d.x_size / float(!d.y_size)    ;Aspect ratio
  930.     dx = p[0] - p[2]
  931.     dy = p[1] - p[3]
  932.     r = sqrt(dx^2 + dy^2)
  933.     if r eq 0.0 then return
  934.     dx = dx/r
  935.     dy = dy/r
  936.     n = 128
  937.     t = findgen(n) * (( 2 * !pi)/ (n-1))    ;Angles..
  938. ;            The whole ball of wax
  939.     xy = [[cos(t) * r], [sin(t) * r/p[18]]] # [[dx, -dy],[dy, dx]]
  940.     xy = reform([p[2] + xy[*,0], p[3] + a*xy[*,1]], n,2,/OVER)
  941.     xy = transpose(xy)
  942.     goto, do_fill
  943.    ENDCASE    ;Circle
  944. 4: BEGIN    ;Box
  945.     xy = p[[[0,1],[0,3],[2,3],[2,1],[0,1]]]
  946.     n = 5
  947.     goto, do_fill
  948.    ENDCASE
  949. else: print,'Unknown object'
  950. ENDCASE
  951.  
  952. end
  953.  
  954.  
  955.  
  956. pro ann_make_draw_button, st
  957. ; Make mode buttons
  958. a = widget_base(st.base, /ROW, Exclusive=!version.os ne 'Win32')
  959. id = lonarr(n_elements(st.mode_buttons))    ;Id arrays
  960.  
  961. wsize = 32
  962. window, xsize=wsize, ysize=wsize, /pix, /free
  963. w2 = wsize/2            ;Handy constants
  964. w9 = 9*wsize/10
  965. w1 = wsize/10
  966. w3 = wsize/3
  967.  
  968. xyouts, w1, wsize/4, 'Abc', /dev, chars= wsize/(3.2* !d.x_ch_size)    ;Text
  969. id[0] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
  970. erase
  971.  
  972. arrow, w1, w3, w9, w3, /device, hsize = 10    ;Arrows/ lines
  973. plots, [w1, w9], wsize - [w3, w3], lines=3, /DEV
  974. id[1] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
  975. erase
  976.  
  977. polyfill, [w1,w2,w1], [w1, w9, w9], /dev    ;Polygons
  978. plots, [ 2, 4, 6, 8, 10, 12, 10, 8, 8]*wsize/16, $    ;Drag/Draw
  979.        [ 1, 3, 7, 9, 9,  11, 16, 14, 6]*wsize/20, /dev
  980. id[2] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
  981. erase
  982.  
  983.  
  984. n = 32                ;Circles
  985. t = findgen(n) *(2 * !pi / (n-1))
  986. plots, wsize/3 * cos(t) + (w2), wsize/3 * sin(t) + w2, /dev
  987. id[3] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
  988. erase
  989.  
  990. plots, [w1,w1, w9, w9, w1], [w1, w9, w9, w1, w1], /dev   ;Squares
  991. id[4] = WIDGET_BUTTON(a, /NO_REL, value= b_button(tvrd()))
  992. erase
  993.  
  994. for i=0, n_elements(id)-1 do $        ;Set up uv = Mn
  995.     WIDGET_CONTROL, id[i], SET_UVALUE='M'+strtrim(i,2)
  996. WIDGET_CONTROL, id[0], /SET_BUTTON    ;Set initial choice
  997. wdelete
  998. st.mode_buttons = id
  999. end
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005. PRO ANNOTATE, DRAWABLE = draw, WINDOW = window, LOAD_FILE=load_file, $
  1006.     COLOR_INDICES=color_indices, TEK_COLORS = tek_colors
  1007.  
  1008. ;+
  1009. ; NAME:
  1010. ;    ANNOTATE
  1011. ;
  1012. ; PURPOSE:
  1013. ;    This procedure is a general purpose drawing program/widget to
  1014. ;    annotate displays. Drawing objects include text, lines, arrows,
  1015. ;    polygons, rectangles, circles, and ellipses.
  1016. ;
  1017. ; CATEGORY:
  1018. ;    Widgets.  Will also work with plain windows.
  1019. ;
  1020. ; CALLING SEQUENCE:
  1021. ;    ANNOTATE
  1022. ;
  1023. ; INPUTS:
  1024. ;    No required inputs.
  1025. ;
  1026. ; KEYWORD PARAMETERS:
  1027. ;    COLOR_INDICES:    an array of color indices from which the user
  1028. ;            can choose colors. For example, to allow the user
  1029. ;            to choose 10 colors, spread evenly over the
  1030. ;            available indices, set the keyword as folows:
  1031. ;            COLOR_INDICES = INDGEN(10) * (!D.N_COLORS-1) / 9
  1032. ;    DRAWABLE:    the widget ID of the draw widget for the annotations.
  1033. ;            This is mutually exclusive with WINDOW.
  1034. ;    LOAD_FILE:    the name of an annotation format file to load after
  1035. ;            initialization.
  1036. ;    TEK_COLORS:    if set, the Tektronix color table is loaded
  1037. ;            starting at color index TEK_COLORS(0), with
  1038. ;            TEK_COLORS(1) color indices. The Tektronix color
  1039. ;            table contains up to 32 distinct colors suitable
  1040. ;            for graphics.
  1041. ;    WINDOW:        the window index number of the window to receive the
  1042. ;            annotations.
  1043. ;
  1044. ; OUTPUTS:
  1045. ;    This procedure has no explicit outputs. Menu choices exist to
  1046. ;    write TIFF, GIF, or PostScript bitmap files. Encapsulated
  1047. ;    or standalone PostScript files may also be created.
  1048. ;
  1049. ; SIDE EFFECTS:
  1050. ;    Annotations are made in the designated window or draw widget.
  1051. ;
  1052. ; RESTRICTIONS:
  1053. ;    This is a simple drawing program.
  1054. ;
  1055. ; PROCEDURE:
  1056. ;    If neither TEK_COLORS or COLOR_INDICES are specified, the default
  1057. ;    is to load 10 colors, evenly distributed over those available.
  1058. ;
  1059. ;    If neither WINDOW or DRAWABLE are specified, the current window
  1060. ;    is used.
  1061. ;
  1062. ; EXAMPLE:
  1063. ;    TVSCL, HANNING(300,200)    ;Output an image in the current window
  1064. ;    ANNOTATE        ;Annotate it
  1065. ;
  1066. ; MODIFICATION HISTORY:
  1067. ;    DMS, RSI, July, 1993.  Original version.
  1068. ;-
  1069.  
  1070.  
  1071.  
  1072. nmodes = 5
  1073. sl_width = 128
  1074.  
  1075. st = { ANN_STATE, $
  1076.     mode : 0, $
  1077.     minor_mode_id: 0L, $
  1078.     minor_mode: 0, $
  1079.     p: fltarr(21), $        ;General params
  1080.     open : 0, $            ;NE 0 if an object is open
  1081.     base : 0L, $
  1082.     current: 0L, $        ;Current object if saved on list
  1083.     objlist : 0L, $        ;object container
  1084.     xy: 0L, $
  1085.     pos:  fltarr(2), $        ;Current position
  1086.     orig: fltarr(2), $        ;Beginning of drag
  1087.     txt: 'Text',$        ;Current contents of text widget
  1088.     mode_buttons: lonarr(nmodes), $
  1089.     mode_bases : lonarr(nmodes+1), $
  1090.     draw_win : 0L, $
  1091.     backing : lonarr(2), $    ;0 = original bitmap, 1 = with closed objects
  1092.     color_id: 0L, $
  1093.     thick_id: 0L, $
  1094.     txt_id: 0L, $
  1095.     txt_size_id: 0L, $
  1096.     txt_ali_id: 0L, $
  1097.     txt_font_id: 0L, $
  1098.     ori_id: 0L, $
  1099.     linestyle_id : 0L, $
  1100.     buttons: 0L, $
  1101.     granularity : 1.0, $
  1102.     instruct: 0L, $
  1103.     line_arrow_id: 0L, $
  1104.     head_size_id: 0L, $
  1105.     fill_mode_id: 0L, $
  1106.     fill_mode: 0, $
  1107.     handle: fltarr(2,4), $
  1108.     temp: fltarr(3), $
  1109.     poly_style: lonarr(nmodes), $
  1110.     poly_angle: lonarr(nmodes), $
  1111.     poly_spacing: lonarr(nmodes), $
  1112.     ecc_id: 0L, $
  1113.     spline_id: 0L, $
  1114.     file: 'annotate.dat', $
  1115.     ihandle: 0, $
  1116.     ps_encap: 0, $
  1117.     ps_color: 0, $
  1118.     ps_orien: 0, $
  1119.     ps_width: 5.0, $
  1120.     ps_units: 0 }
  1121.  
  1122. st.p[18] = 1.0    ;Eccent
  1123.  
  1124. ann_base = WIDGET_BASE(title='Annotate', /COLUMN)    ;Our base
  1125. st.base = ann_base
  1126.  
  1127. win = !d.window        ;Default window
  1128. use_xmgr = 0
  1129. if n_elements(window) eq 1 then win = window $
  1130. else if n_elements(draw) eq 1 then begin
  1131.     widget_control, draw, get_value = win, EVENT_PRO = 'ANN_DRAW_EVENT', $
  1132.         SET_UVALUE = ann_base
  1133.     use_xmgr = 1
  1134.     endif
  1135. if win lt 0 then message,'No draw window active'
  1136. st.draw_win = win
  1137.  
  1138.  
  1139. wset, win
  1140. nx = !d.x_size
  1141. ny = !d.y_size
  1142.  
  1143. for i=0,1 do begin        ;Get 2 backing pixmaps.
  1144.     window, /FREE, /PIXMAP, xs = nx, ys = ny
  1145.     device, copy = [0,0,nx, ny, 0, 0, win]  ;Copy original window
  1146.     st.backing[i] = !d.window
  1147.     endfor
  1148.  
  1149.  
  1150. st.objlist = WIDGET_BASE(ann_base, UVALUE = 0, $    ;Object list holder
  1151.     xsize = 2, ysize=2)
  1152. st.xy = WIDGET_BASE(ann_base, UVALUE = 0, $    ;Polygon points holder
  1153.     xsize = 2, ysize=2)
  1154.  
  1155. junk = WIDGET_BASE(ann_base, /ROW)
  1156. tmp = CW_PDMENU(junk, /RETURN_NAME, UVALUE='TOPROW', [ $
  1157.     {CW_PDMENU_S, flags: 1, name: 'File'}, $
  1158.     {CW_PDMENU_S, flags: 0, name: 'Load'}, $
  1159.     {CW_PDMENU_S, flags: 0, name: 'Save'}, $
  1160.     {CW_PDMENU_S, flags: 0, name: 'Save As'}, $
  1161.     {CW_PDMENU_S, flags: 1, name: 'Write PostScript'}, $
  1162.     {CW_PDMENU_S, flags: 0, name: 'Everything'}, $
  1163.     {CW_PDMENU_S, flags: 2, name: 'Objects only'}, $
  1164.     {CW_PDMENU_S, flags: 1, name: 'Export Bitmap'}, $
  1165.     {CW_PDMENU_S, flags: 0, name: 'GIF'}, $
  1166.     {CW_PDMENU_S, flags: 0, name: 'PostScript'}, $
  1167.     {CW_PDMENU_S, flags: 2, name: 'TIFF'}, $
  1168.     {CW_PDMENU_S, flags: 0, name: 'Clear'}, $
  1169.     {CW_PDMENU_S, flags: 2, name: 'Exit'}])
  1170.  
  1171. tmp = WIDGET_BUTTON(junk, value='Help', UVALUE='Help', /NO_REL)
  1172. tmp = WIDGET_BUTTON(junk, value='Options', UVALUE='Options', /NO_REL)
  1173.  
  1174. junk = WIDGET_BASE(ann_base, /ROW)
  1175. tmp = WIDGET_BUTTON(junk, VALUE='Save', UVALUE='Save', /NO_REL)
  1176. st.minor_mode_id = CW_BGROUP(junk, /EXCLUSIVE, /ROW, /NO_REL, $
  1177.     LABEL_LEFT = 'Mode:', $
  1178.     ['Draw', 'Edit','Select'], $
  1179.         UVALUE='@Rst.minor_mode = ev.value', SET_VALUE=0)
  1180.  
  1181. ann_make_draw_button, st
  1182.  
  1183. scolor = 0
  1184. ncolors = 10
  1185. if n_elements(tek_colors) ge 1 then begin
  1186.     scolor = tek_colors[0]
  1187.     if n_elements(tek_colors) ge 2 then ncolors = tek_colors[1] else ncolors=8
  1188.     color_indices = indgen(ncolors) + scolor
  1189. endif else if n_elements(color_indices) gt 0 then begin
  1190.     ncolors = n_elements(color_indices)
  1191. endif else begin
  1192.     color_indices = (!d.n_colors-1) * lindgen(ncolors) / (ncolors-1)
  1193. endelse
  1194.  
  1195. st.color_id = CW_CLR_INDEX(ann_base, LABEL = 'Color:', XSIZE=160, $
  1196.     NCOLORS = ncolors, START_COLOR = scolor, $
  1197.     COLOR_VALUES=color_indices, uvalue = '@Rst.p[4] = ev.value')
  1198. st.p[4] = color_indices[ncolors-1]    ;Init color
  1199.  
  1200. junk = WIDGET_BASE(ann_base, /ROW)
  1201. st.linestyle_id = CW_BSELECTOR(junk, LABEL_TOP = 'Linestyle:', $
  1202.     ['Solid', 'Dots', 'Dashes', 'Dash-Dot', 'Dash-3 Dots', $
  1203.     'Long Dash'], uvalue = '@Rst.p(6) = ev.value')
  1204. st.thick_id = WIDGET_SLIDER(junk, XSIZE = sl_width, MIN=0, $
  1205.     MAX=200, TITLE = 'Thickness', /DRAG, $
  1206.     UVALUE='@Rst.p(5) = ev.value/10.')
  1207.  
  1208. st.instruct = WIDGET_TEXT(ann_base, value = ' ', xsize=32, ysize=1, /FRAME)
  1209.  
  1210. tmp_base = WIDGET_BASE(ann_base, /FRAME)
  1211. for i=0, nmodes do BEGIN
  1212.     base = WIDGET_BASE(tmp_base, /COLUMN)
  1213.     st.mode_bases[i] = base
  1214.     WIDGET_CONTROL, base, map = i eq 0
  1215.     ENDFOR
  1216.  
  1217. ;
  1218. ; Text widget
  1219. top = st.mode_bases[0]
  1220. junk = WIDGET_BASE(top, /ROW)
  1221. junk1 = WIDGET_LABEL(junk, Value = 'Text: ')
  1222. st.txt_id = WIDGET_TEXT(junk, xs = 32, ys = 1, /frame, /edit, $
  1223.     uvalue = '#Rst.txt=v[0]', value=st.txt)
  1224. junk = WIDGET_BASE(top, /ROW)
  1225. st.txt_size_id = WIDGET_SLIDER(junk, TITLE='Size', Value=0, XSIZE = sl_width, $
  1226.     MAX=100, MIN=0, /DRAG, UVALUE = '@Rst.p[7] = ev.value/10.')
  1227. st.ori_id = WIDGET_SLIDER(junk, TITLE='Orientation', Value = 0, $
  1228.     MAX = 360, min = 0, /DRAG, UVALUE = '@Rst.p[14] = ev.value', $
  1229.     XSIZE = sl_width)
  1230.  
  1231. st.txt_ali_id = CW_BGROUP(top, LABEL_LEFT='Alignment:', /EXCLUSIVE, /ROW, $
  1232.     ['Left', 'Center', 'Right' ], SET_VALUE=0, /NO_REL, $
  1233.     UVALUE='@Rst.p[12] = ev.value/2.')
  1234. fonts = ['Simplex Roman  ', 'Simplex Greek', 'Duplex Roman', $
  1235.     'Complex Roman', 'Complex Greek', 'Complex Italic', $
  1236.     'Math & Special', 'Special', 'Gothic', 'Script', 'Complex Script', $
  1237.     'Gothic Italian', 'Gothic German', 'Cyrillic', 'Triplex Roman', $
  1238.     'Triplex Italic']
  1239. junk = WIDGET_BASE(top, /ROW)
  1240. junk1 = WIDGET_LABEL(junk, VALUE = 'Font:')
  1241. st.txt_font_id = CW_BSELECTOR(junk, fonts, $
  1242.     uvalue = '@Rst.p[13] = ev.value + 3')
  1243.  
  1244. ; Arrow Widget
  1245. top = st.mode_bases[1]
  1246. junk = WIDGET_BASE(top, /ROW)
  1247. st.line_arrow_id = CW_BGROUP(junk, /EXCLUSIVE, /ROW, /NO_REL, $
  1248.     ['Line','Arrow', 'Solid Arrow'],  $
  1249.     UVALUE='@Rst.p[17]=ev.value', SET_VALUE=0, LABEL_LEFT='Mode:')
  1250. st.head_size_id = WIDGET_SLIDER(top, TITLE='Head Size', VALUE = 0, $
  1251.     XSIZE=sl_width, MAX=100, /DRAG, UVALUE='@Rst.p[15] = ev.value/10.')
  1252.  
  1253.  
  1254.     
  1255. ; Polygon Widget
  1256. top = st.mode_bases[2]
  1257. st.fill_mode_id = CW_BGROUP(top, /EXCLUSIVE, /ROW, LABEL_LEFT = 'Mode:', $
  1258.     ['Draw', 'Edit', 'Delete'], /NO_REL, $
  1259.         UVALUE='@Rst.fill_mode = ev.value', SET_VALUE=0)
  1260.  
  1261.  
  1262. ; Circle widget
  1263. top = st.mode_bases[3]
  1264. st.ecc_id = WIDGET_SLIDER(top, TITLE='Eccentricity', VALU=0, /DRAG, $
  1265.         XSIZE=sl_width, MIN=0, MAX=100, UVALUE='@Rst.p[18]=ev.value/10.+1')
  1266.  
  1267. ; Square Widget  has nothing unique
  1268.  
  1269.  
  1270. for i=2, nmodes-1 do  begin    ;Make polyfill controls for the bases
  1271.     top = st.mode_bases[i]
  1272.     st.poly_style[i] = CW_BGROUP(top, /EXCLUSIVE, /ROW, $
  1273.     LABEL_LEFT = 'Fill: ', /NO_REL, $
  1274.     ['None', 'Solid', 'Lines'],  $
  1275.         UVALUE='@Rst.p[8]=ev.value', SET_VALUE=0)
  1276.     if i eq 2 then $
  1277.     st.spline_id = CW_BGROUP(top, /EXCLUSIVE, /ROW, /NO_REL, $
  1278.         LABEL_LEFT='Interpolation:', ['None', 'Spline'], $
  1279.         UVALUE='@Rst.p[20]=ev.value', SET_VALUE=0)
  1280.     junk = WIDGET_BASE(top, /ROW)
  1281.     st.poly_angle[i] = WIDGET_SLIDER(junk, TITLE='Line Angle', VALU=0, /DRAG, $
  1282.         MIN=0, MAX=180, UVALUE='@Rst.p[10]=ev.value', XSIZE=sl_width)
  1283.     st.poly_spacing[i] = WIDGET_SLIDER(junk, TITLE='Line Spacing', $
  1284.     VALU=0, /DRAG, MIN=0, MAX=50, UVALUE='@Rst.p[9]=ev.value', $
  1285.     XSIZE=sl_width)
  1286.     endfor
  1287.  
  1288.  
  1289.  
  1290. ;*******************************************
  1291.  
  1292.  
  1293.  
  1294. ; Options Widget
  1295. top = st.mode_bases[nmodes]
  1296. junk = WIDGET_BASE(top, /ROW)
  1297. junk1 = WIDGET_LABEL(junk, value = 'Grid granularity:')
  1298. junk1 = WIDGET_TEXT(junk, /EDIT, /FRAME, xsize=4, ysize=1, value='1', $
  1299.     UVALUE = '# st.granularity=ann_get_num(v(0), ev.id, 1, 128)')
  1300.  
  1301. junk = WIDGET_BASE(top, /COLUMN, /FRAME)
  1302. junk1 = WIDGET_LABEL(junk, VALUE= 'PostScript Options')
  1303. junk1 = WIDGET_BASE(junk, /ROW)
  1304. junk2 = CW_BGROUP(junk1, /EXCLUSIVE, /ROW, /NO_REL, $
  1305.     ['Std', 'Encapsulated'], $
  1306.     UVALUE='@ st.ps_encap=ev.value', SET_VALUE=0)
  1307. junk2 = CW_BGROUP(junk1, /EXCLUSIVE, /ROW, /NO_REL, $
  1308.     ['Mono', 'Color'], $
  1309.     UVALUE='@ st.ps_color=ev.value', SET_VALUE=0)
  1310. junk1 = CW_BGROUP(junk, /EXCLUSIVE, /ROW, /NO_REL, $
  1311.     ['Portrait', 'Landscape'], $
  1312.     UVALUE='@ st.ps_orien=ev.value', SET_VALUE=0)
  1313. junk1 = WIDGET_BASE(junk, /ROW)
  1314. junk2 = WIDGET_LABEL(junk1, VALUE='Width:')
  1315. junk2 = WIDGET_TEXT(junk1, /EDIT, /FRAME, xsize=5, ysize=1, value='5.0', $
  1316.     UVALUE='# st.ps_width=ann_get_num(v(0), ev.id, .1, 200)')
  1317. junk2 = CW_BGROUP(junk1, /EXCLUSIVE, /ROW, /NO_REL, $
  1318.     ['In', 'Cm'], UVALUE='@ st.ps_units=ev.value', SET_VALUE=0)
  1319.  
  1320. junk = WIDGET_BUTTON(top, VALUE='Dismiss', UVALUE='Dismiss')
  1321.  
  1322.  
  1323. wset, win
  1324. wshow, win
  1325.  
  1326. if n_elements(tek_colors) ge 1 then tek_color, scolor, ncolors
  1327.  
  1328. widget_control, ann_base, /REAL
  1329. WIDGET_CONTROL, st.color_id, SET_VALUE = color_indices[ncolors-1]   ;Set color
  1330.  
  1331. if keyword_set(load_file) then $    ;Load a file?
  1332.     ann_xfer_file, st, load_file, /LOAD
  1333. WIDGET_CONTROL, ann_base, SET_UVALUE = st, /NO_COPY
  1334.  
  1335. if use_xmgr then xmanager, 'annotate', ann_base $
  1336. else xmgr_fake, ann_base, win
  1337. end
  1338.